home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / CW GUSI 1.6.4 / src / GUSIINET.cp < prev    next >
Text File  |  1995-11-05  |  13KB  |  571 lines

  1. /*********************************************************************
  2. Project    :    GUSI                -    Grand Unified Socket Interface
  3. File        :    GUSIINET.cp        -    TCP/IP Sockets, general routines
  4. Author    :    Matthias Neeracher
  5.  
  6.     This file was derived from the socket library by 
  7.     
  8.         Charlie Reiman    <creiman@ncsa.uiuc.edu> and
  9.         Tom Milligan    <milligan@madhaus.utcs.utoronto.ca>
  10.           
  11. Language    :    MPW C/C++
  12.  
  13. $Log: GUSIINET.cp,v $
  14. Revision 1.4  1994/12/30  20:05:05  neeri
  15. Remove built in INETd support.
  16. Wake up process from completion routines to improve performance.
  17. Fix problem occurring when a netdb routine was called before the first socket().
  18.  
  19. Revision 1.3  1994/08/10  00:26:25  neeri
  20. Sanitized for universal headers.
  21.  
  22. Revision 1.2  1994/05/01  23:39:54  neeri
  23. Defer PB allocation.
  24.  
  25. Revision 1.1  1994/03/08  22:06:58  neeri
  26. Initial revision
  27.  
  28. Revision 0.7  1993/10/31  00:00:00  neeri
  29. Deferred opening of MacTCP Services
  30.  
  31. Revision 0.6  1993/07/17  00:00:00  neeri
  32. htonl and friends
  33.  
  34. Revision 0.5  1993/02/07  00:00:00  neeri
  35. New configuration technique
  36.  
  37. Revision 0.4  1993/01/31  00:00:00  neeri
  38. Inetd support
  39.  
  40. Revision 0.3  1992/08/23  00:00:00  neeri
  41. INETSocket::Available()
  42.  
  43. Revision 0.2  1992/08/20  00:00:00  neeri
  44. Wrote most of the functions
  45.  
  46. Revision 0.1  1992/08/16  00:00:00  neeri
  47. Split
  48.  
  49. *********************************************************************/
  50.  
  51. #include "GUSIINET_P.h"
  52. #include <machine/endian.h>
  53.  
  54. #include <AddressXlation.h>
  55. #include <LowMem.h>
  56.  
  57. #if GENERATING68K
  58. #pragma segment GUSIINET
  59. #endif
  60.  
  61. /***************************** Globals ******************************/
  62.  
  63. #define NUM_PBS    32
  64.  
  65. INETSocketDomain    INETSockets;
  66.  
  67. /***************************** Peanuts ******************************/
  68.  
  69. unsigned long (htonl)(unsigned long h)
  70. {
  71.     return h;
  72. }
  73.  
  74. unsigned short    (htons)(unsigned short h)
  75. {
  76.     return h;
  77. }
  78.  
  79. unsigned long (ntohl)(unsigned long n)
  80. {
  81.     return n;
  82. }
  83.  
  84. unsigned short    (ntohs)(unsigned short n)
  85. {
  86.     return n;
  87. }
  88.  
  89. /***************** Our resident MacTCP error expert *****************/
  90.  
  91. /*
  92.  * Convert a MacTCP err code into a unix error code.
  93.  */
  94.  
  95. int TCP_error(int MacTCPerr)
  96. {
  97.     switch ( MacTCPerr ) {
  98.     case 0:
  99.         return 0;
  100.     case ipBadLapErr:
  101.     case ipBadCnfgErr:
  102.     case ipNoCnfgErr:
  103.     case ipLoadErr:
  104.     case ipBadAddr:
  105.         errno = ENXIO;            /* device not configured */    /* a cheap cop out */
  106.         break;
  107.     case connectionClosing:
  108.         errno = ESHUTDOWN;        /* Can't send after socket shutdown */
  109.         break;
  110.     case connectionExists:
  111.         errno = EISCONN;        /* Socket is already connected */
  112.         break;
  113.     case connectionTerminated:
  114.         errno = ENOTCONN;        /* Connection reset by peer */  /* one of many possible */
  115.         break;
  116.     case openFailed:
  117.         errno = ECONNREFUSED;    /* Connection refused */
  118.         break;
  119.     case duplicateSocket:        /* technically, duplicate port */
  120.         errno = EADDRINUSE;        /* Address already in use */
  121.         break;
  122.     case ipDestDeadErr:
  123.         errno = EHOSTDOWN;        /* Host is down */
  124.         break;
  125.     case ipRouteErr:
  126.         errno = EHOSTUNREACH;    /* No route to host */
  127.         break;
  128.     default:
  129.         errno = MacTCPerr > 0 ? MacTCPerr : EINVAL;        /* cop out; an internal err, unix err, or no err */
  130.         break;
  131.     }
  132.  
  133.     return -1;
  134. }
  135.  
  136. /************************ INETSocket members ************************/
  137.  
  138. INETSocket::INETSocket()
  139.     : Socket()
  140. {
  141.     bzero(&sa, sizeof(struct sockaddr_in));
  142.     bzero(&peer, sizeof(struct sockaddr_in));
  143.     
  144.     sa.sin_family    = AF_INET;
  145.     sa.sin_len        = sizeof(struct sockaddr_in);
  146.     status            = SOCK_STATUS_USED;
  147.     nonblocking        = false;    
  148.  
  149. #if !GENERATINGCFM
  150.     processA5        = LMGetCurrentA5();
  151. #endif    
  152.     
  153.     INETSockets.OpenSocket();
  154. }
  155.  
  156. #if !GENERATINGCFM
  157.  
  158. void INETSocket::Ready() 
  159. {
  160.     long saveA5 = SetA5(long(processA5));
  161.     
  162.     INETSockets.Ready();
  163.     
  164.     SetA5(saveA5);
  165. }
  166.  
  167. #endif    
  168.  
  169. INETSocket::INETSocket(StreamPtr stream)
  170.     : Socket(), stream(stream)
  171. {
  172.     sa.sin_family    = AF_INET;
  173.     sa.sin_len        = sizeof(struct sockaddr_in);
  174.     peer.sin_family= AF_INET;
  175.     peer.sin_len    = sizeof(struct sockaddr_in);
  176.     status            = SOCK_STATUS_USED;
  177.     sstate            = SOCK_STATE_CONNECTED;
  178.     nonblocking        = false;    
  179.     
  180.     INETSockets.OpenSocket();
  181.  
  182. #if !GENERATINGCFM
  183.     processA5        = LMGetCurrentA5();
  184. #endif    
  185. }
  186.  
  187. INETSocket::~INETSocket()
  188. {
  189.     INETSockets.CloseSocket();
  190. }
  191.  
  192. unsigned long INETSocket::Available()
  193. {
  194.     return 0;
  195. }
  196.  
  197. /*
  198.  *    bind(name, namelen)
  199.  *
  200.  *        bind requests that the name (ip address and port) pointed to by 
  201.  *        name be assigned to the socket.
  202.  *        
  203.  *        The return value is 0 on success or -1 if an error occurs,
  204.  *        in which case global variable errno is set to one of:
  205.  *
  206.  *        EAFNOSUPPORT        The address family in name is not AF_INET.
  207.  *        
  208.  *        EINVAL              The socket is already bound to an address.
  209.  *        
  210.  *        EADDRNOTAVAIL       The specified address is  not  available
  211.  *                             from the local machine. ie. the address
  212.  *                            portion of name was not this machine's address.
  213.  *
  214.  *        MacTCP does not separate name binding and connection establishment.
  215.  *        Therefore the port number is not verified, just stored for later use.
  216.  *
  217.  *        If a specific local port is not required, bind is optional in this
  218.  *        implementation.
  219.  */    
  220.  
  221. int INETSocket::bind(void * addr, int namelen)
  222. {
  223.     struct sockaddr_in *name    =    (struct sockaddr_in *)addr;
  224.         
  225.     if (namelen < int(sizeof(struct sockaddr_in)))
  226.         return GUSI_error(EINVAL);
  227.  
  228.     if (name->sin_family != AF_INET)
  229.         return GUSI_error(EAFNOSUPPORT);
  230.  
  231.     if (sa.sin_port != 0) /* already bound */
  232.         return GUSI_error(EINVAL);
  233.  
  234.     /*
  235.      *    If client passed a local IP address, assure it is the right one
  236.      */
  237.     if (name->sin_addr.s_addr != 0) 
  238.     {
  239.         struct GetAddrParamBlock pbr;
  240.         
  241.         pbr.ioCRefNum     = INETSockets.Driver();
  242.         pbr.csCode         = ipctlGetAddr;
  243.         
  244.         if (PBControlSync(ParmBlkPtr(&pbr)))
  245.             return GUSI_error(ENETDOWN);
  246.         
  247.         if (name->sin_addr.s_addr != pbr.ourAddress)
  248.             return GUSI_error(EADDRNOTAVAIL);
  249.     }
  250.  
  251.     /*
  252.      *    NOTE: can't check a TCP port for EADDRINUSE
  253.      *    just save the address and port away for connect or listen or...
  254.      */
  255.     sa.sin_addr.s_addr     = name->sin_addr.s_addr;
  256.     sa.sin_port             = name->sin_port;
  257.     
  258.     return 0;
  259. }
  260.  
  261. /*
  262.  *    getsockname(name, namelen)
  263.  *
  264.  *        getsockname returns the current name for the  socket.
  265.  *        Namelen should  be initialized to
  266.  *        indicate the amount of space pointed to by name.  On  return
  267.  *        it contains the actual size of the name returned (in bytes).
  268.  *        
  269.  *        A 0 is returned if the call succeeds, -1 if it fails.
  270.  */
  271.  
  272. int INETSocket::getsockname(void *name, int *namelen)
  273. {
  274.     if (*namelen < 0)
  275.         return GUSI_error(EINVAL);
  276.  
  277.     memcpy(name, &sa, *namelen = min(*namelen, int(sizeof(struct sockaddr_in))));
  278.     return 0;
  279. }
  280.  
  281. /*
  282.  *    getpeername(name, namelen)
  283.  *
  284.  *        getpeername returns the name of the peer connected to socket.
  285.  *
  286.  *        The  int  pointed  to  by the namelen parameter
  287.  *        should be  initialized  to  indicate  the  amount  of  space
  288.  *        pointed  to  by name.  On return it contains the actual size
  289.  *        of the name returned (in bytes).  The name is  truncated  if
  290.  *        the buffer provided is too small.
  291.  *        
  292.  *        A 0 is returned if the call succeeds, -1 if it fails.
  293.  */
  294.  
  295. int INETSocket::getpeername(void *name, int *namelen)
  296. {
  297.     if (*namelen < 0)
  298.         return GUSI_error(EINVAL);
  299.  
  300.     memcpy(name, &peer, *namelen = min(*namelen, int(sizeof(struct sockaddr_in))));
  301.     return 0;
  302. }
  303.  
  304. /*
  305.  *    shutdown(how)
  306.  *
  307.  *        shutdown call causes all or part of a full-duplex
  308.  *        connection on the socket to be shut down.  If
  309.  *        how is 0, then further receives will be disallowed.  If  how
  310.  *        is  1,  then further sends will be disallowed.  If how is 2,
  311.  *        then further sends and receives will be disallowed.
  312.  *        
  313.  *        A 0 is returned if the call succeeds, -1 if it fails.
  314.  */
  315.  
  316. int INETSocket::shutdown(int how)
  317. {
  318.     switch(how) {
  319.     case 0 : 
  320.         status |= SOCK_STATUS_NOREAD;
  321.         break;
  322.     case 1 : 
  323.         status |= SOCK_STATUS_NOWRITE;
  324.         break;
  325.     case 2 :
  326.         status |= SOCK_STATUS_NOREAD | SOCK_STATUS_NOWRITE;
  327.         break;
  328.     default :
  329.         return GUSI_error(EINVAL);
  330.     }
  331.     
  332.     return 0;
  333. }
  334.  
  335. /*
  336.  *    fcntl() operates on the socket according to the order in cmd:
  337.  *
  338.  *        F_GETFL    returns the descriptor status flags. The only
  339.  *                flag supported is FNDELAY for non-blocking i/o.
  340.  *
  341.  *        F_SETFL    sets descriptor status flags. The only
  342.  *                 flag supported is FNDELAY for non-blocking i/o.
  343.  *
  344.  *        Upon successful completion, the value  returned  depends  on
  345.  *        cmd as follows:
  346.  *         F_GETFL   Value of flags.
  347.  *            F_SETFL   0.
  348.  *
  349.  *        On error, a value of -1  is returned and errno is set to indicate 
  350.  *        the error.
  351.  *
  352.  *        EBADF           s is not a valid open descriptor.
  353.  *
  354.  *        EMFILE          cmd is F_DUPFD and socket descriptor table is full.
  355.  *
  356.  *        EINVAL          cmd is F_DUPFD and arg  is  negative  or
  357.  *                      greater   than   the  maximum  allowable
  358.  *                      number (see getdtablesize).
  359.  */
  360. int INETSocket::fcntl(unsigned int cmd, int arg)
  361. {
  362.     switch(cmd) {
  363.     /*
  364.      *  Get socket status.  This is like getsockopt().
  365.      *  Only supported descriptor status is FNDELAY.
  366.      */
  367.     case F_GETFL : 
  368.         if (nonblocking)
  369.             return FNDELAY;
  370.         else
  371.             return 0;
  372.     /*
  373.      *  Set socket status.  This is like setsockopt().
  374.      *  Only supported descriptor status is FNDELAY.
  375.      */
  376.     case F_SETFL : 
  377.         if (arg & FNDELAY)
  378.             nonblocking = true;
  379.         else
  380.             nonblocking = false;
  381.         
  382.         return 0;
  383.     default:
  384.         return GUSI_error(EOPNOTSUPP);
  385.     }
  386. }
  387.  
  388. int INETSocket::ioctl(unsigned int request, void *argp)
  389. {
  390.     struct ifreq *    ifr;
  391.     int                size;
  392.     
  393.     /*
  394.      * Interpret high order word to find amount of data to be copied 
  395.      * to/from the user's address space.
  396.      */
  397.     size =(request &~(IOC_INOUT | IOC_VOID)) >> 16;
  398.     
  399.     /*
  400.      * Zero the buffer on the stack so the user gets back something deterministic.
  401.      */
  402.     if ((request & IOC_OUT) && size)
  403.         bzero((Ptr)argp, size);
  404.  
  405.     ifr =(struct ifreq *)argp;
  406.     switch(request) {
  407.     /* Non-blocking I/O */
  408.     case FIONBIO:
  409.         nonblocking = (Boolean) *(int *) argp;
  410.         return 0;
  411.     /* Number of bytes on input Q */
  412.     case FIONREAD:
  413.         *(unsigned long *) argp    = Available();
  414.         
  415.         return 0;
  416.     default :
  417.         return GUSI_error(EOPNOTSUPP);
  418.     }
  419. }
  420.  
  421. /********************* INETSocketDomain members *********************/
  422.  
  423. extern "C" void GUSIwithInternetSockets()
  424. {
  425.     INETSockets.DontStrip();
  426. }
  427.  
  428. INETSocketDomain::INETSocketDomain()
  429.     :    SocketDomain(AF_INET)    
  430. {
  431.     GUSIConfiguration    conf;        // GUSIConfig isn't yet guaranteed to work    
  432.     
  433.     driverState        = 1;
  434.     resolverState    = 1;
  435.     drvrRefNum         = 0;
  436.     inetCount        = 0;
  437.     
  438.     /* allocate storage for pbs */
  439.     pbLast = 0;
  440.     pbList = nil;
  441. }
  442.  
  443. short INETSocketDomain::Driver()
  444. {
  445.     ParamBlockRec         pb; 
  446.  
  447.     if (driverState == 1) {
  448.         pb.ioParam.ioCompletion    = 0L; 
  449.         pb.ioParam.ioNamePtr     = (StringPtr) "\p.IPP"; 
  450.         pb.ioParam.ioPermssn     = fsCurPerm;
  451.         
  452.         driverState                 = PBOpenSync(&pb);
  453.         drvrRefNum                     = pb.ioParam.ioRefNum; 
  454.     }
  455.     
  456.     return driverState ? 0 : drvrRefNum;
  457. }
  458.  
  459. OSErr    INETSocketDomain::Resolver()
  460. {
  461.     Driver();
  462.     if (resolverState == 1)
  463.         resolverState = OpenResolver(nil);
  464.     
  465.     return resolverState;
  466. }
  467.  
  468. /*
  469.  *    INETSocketDomain::socket(type, protocol)
  470.  *
  471.  *        Create a MacTCP socket and return a descriptor.
  472.  *
  473.  *        Type may be SOCK_STREAM to create a TCP socket or 
  474.  *        SOCK_DGRAM to create a UDP socket.
  475.  *                 
  476.  *        Protocol is ignored. (isn't it always?)
  477.  *                 
  478.  *        TCP sockets provide sequenced, reliable, two-way connection
  479.  *        based byte streams.
  480.  *
  481.  *        A TCP socket must be in a connected
  482.  *        state before any data may be sent or received on it. A 
  483.  *        connection to another socket is created with a connect() call
  484.  *        or the listen() and accept() calls.
  485.  *        Once connected, data may be transferred using read() and
  486.  *        write() calls or some variant of the send() and recv()
  487.  *        calls. When a session has been completed a close() may  be
  488.  *        performed.
  489.  *
  490.  *        
  491.  *        A UDP socket supports the exchange of datagrams (connectionless, 
  492.  *        unreliable messages of a fixed maximum length) with  
  493.  *        correspondents named in send() calls. Datagrams are
  494.  *        generally received with recv(), which returns the next
  495.  *        datagram with its return address.
  496.  *
  497.  *        An fcntl() or ioctl() call can be used to enable non-blocking I/O.
  498.  *
  499.  *        The return value is a descriptor referencing the socket or -1
  500.  *        if an error occurs, in which case global variable errno is
  501.  *        set to one of:
  502.  *
  503.  *            ENOMEM                    Failed to allocate memory for the socket
  504.  *                              data structures.
  505.  *
  506.  *            ESOCKTNOSUPPORT     Type wasn't SOCK_STREAM or SOCK_DGRAM.
  507.  *
  508.  *            EMFILE              The socket descriptor table is full.
  509.  */
  510.  
  511. Socket * INETSocketDomain::socket(int type, short)
  512. {
  513.     INETSocket * sock    =    nil;
  514.     
  515.     errno    =    0;
  516.  
  517.     if (!Driver())
  518.         return (Socket *) GUSI_error_nil(ENETDOWN);
  519.         
  520.     switch (type)    {
  521.     case SOCK_STREAM:
  522.         sock = new TCPSocket();
  523.         break;
  524.     case SOCK_DGRAM:
  525.         sock = new UDPSocket();
  526.         break;
  527.     default:
  528.         GUSI_error(ESOCKTNOSUPPORT);
  529.     }    
  530.     
  531.     if (sock && errno)    {
  532.         delete sock;
  533.         
  534.         return nil;
  535.     } 
  536.         
  537.     return sock;
  538. }
  539.  
  540. AnnotatedPB * INETSocketDomain::GetPB()
  541. {
  542.     AnnotatedPB    *    curPB;
  543.     
  544.     do {
  545.         if (++pbLast == NUM_PBS)
  546.             pbLast = 0;
  547.         
  548.         curPB = pbList + pbLast;
  549.     } while (curPB->Busy());
  550.     
  551.     return curPB;
  552. }
  553.  
  554. void INETSocketDomain::OpenSocket()
  555. {
  556.     if (!inetCount++) 
  557.         if (!(pbList = new AnnotatedPB[NUM_PBS])) {
  558.             errno             =     ENOMEM;
  559.             inetCount    =    0;
  560.         }
  561. }
  562.  
  563. void INETSocketDomain::CloseSocket()
  564. {
  565.     if (!--inetCount) {
  566.         delete [] pbList;
  567.         
  568.         pbList = nil;
  569.     }
  570. }
  571.